#include <iostream>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>
#include <queue>
#include<time.h>
#include <signal.h>

sem_t sem_queue;

void up()
{
    sem_post(&sem_queue);
}

void down()
{
    sem_wait(&sem_queue);
}

std::queue<int> sockets;

sem_t sem_timer;

size_t working_tasks = 0;

clock_t start = 0;
clock_t working_time;
clock_t start_working;
double worked_seconds;
double result;

void startTimer()
{
    sem_wait(&sem_timer);

    if (working_tasks++ == 0)
    {
        //start time interval
        //std::cout << "Start time" << std::endl;
        start_working = clock();
        //start all time:
        if (start == 0)
            start = start_working;
    }

    sem_post(&sem_timer);
}

void stopTimer()
{
    sem_wait(&sem_timer);

    if (--working_tasks == 0)
    {
        //stop time interval
        working_time += clock() - start_working;
        start_working = 0;
        //std::cout << "Stop time" << std::endl;
    }

    sem_post(&sem_timer);
}

void calculateServerCapacity()
{
    //calc current interval:
    clock_t now = clock();
    clock_t interval = now - start;
    //divide current working time plus not finished working interval to the program working time:
    result = 1.0 * (working_time + (start_working == 0 ? 0 : now - start_working)) / interval;
}

size_t treatment_time;

void sleep()
{
    clock_t endwait = clock() + treatment_time * CLOCKS_PER_SEC / 1000;
    while (clock() < endwait)
    {
        //hard work...
        clock_t c = clock();
    }
}

void* serverLogic(void* data/*size_t queue_len, size_t thread_count, size_t treatment_time, size_t port*/)
{
    size_t* number = (size_t*)data;

    //std::cout << "Thread #" << *number << " started" << std::endl;

    while(true)
    {
        //client socket:
        int cli_fd = 0;

        //get request from the vector:
        down();
        if (sockets.size())
        {
            cli_fd = sockets.front();
            sockets.pop();
        }
        up();

        //is there any tasks?
        if (cli_fd != 0)
        {
            //std::cout << "Thread #" << *number << " has got a task" << std::endl;

            char buffer[1024];
            int length = recv(cli_fd, buffer, 1024, 0);
            if (length < 0)
            {
                std::cout << "Server's received empty message" << std::endl;
                close(cli_fd);
                continue;
            }

            //std::cout << "Message - " << buffer << std::endl;

            char message[] = "Server answer";
            send(cli_fd, message, strlen(message) + 1, 0);

            close(cli_fd);

            //stop time interval - server may stop!
            stopTimer();

            //calculate current percent:
            calculateServerCapacity();
        }
        sleep();        
    }
    return 0;
}

void handler(int param)
{
    std::cout << "Server capacity: " << result << std::endl;
    exit(0);
}

int main (int argc, char** argv)
{
        if (argc <= 4)
        {
                std::cout << "Usage: serverUtil queue_len thread_count treatment_time port" << std::endl;
                return 1;
        }

        size_t queue_len = atoi(argv[1]);
        std::cout << "Queue length: " << queue_len << std::endl;

        size_t thread_count = atoi(argv[2]);
        std::cout << "Amount of threads: " << thread_count << std::endl;

        /*size_t*/ treatment_time = atoi(argv[3]);
        std::cout << "Treatment time: " << treatment_time << std::endl;

        size_t port = atoi(argv[4]);
        std::cout << "Port: " << port << std::endl;

        //initiate semaphore
        if (sem_init(&sem_queue, 0, 1) == -1 || sem_init(&sem_timer, 0, 1) == -1)
        {
                std::cout << "Unable to create semaphores!" << std::endl;
                return 4;
        }

        //bind signal handler: it will return result to the user
        signal(SIGINT, handler);

        //create thread pool:
        //for - lots of threads with the same server logic
        pthread_t th[thread_count];
        for (size_t i = 0; i < thread_count; ++i)
        {
            size_t* number = new size_t(i);
            pthread_create(&th[i], 0, serverLogic, (void*)number);
        }
        //startServer(queue_len, thread_count, treatment_time, port);
        //all these threads are infinite, so we don't need to join them

        //start socket logic:
        int sock_fd;

        //open socket
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                std::cout << "Error while creating socket! Server can't start." << std::endl;
                return 1;
        }

        sockaddr_in servaddr;
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(port);

        if (bind(sock_fd, (sockaddr*) &servaddr, sizeof(servaddr)))
        {
            std::cout << "Binding error!" << std::endl;
            return 2;
        }

        if (listen(sock_fd, 1))
        {
                std::cout << "Listen error!!! Server can't start." << std::endl;
                return 3;
        }

        while(true)
        {
            sockaddr_in cliaddr;
            size_t size = sizeof (cliaddr);

            //std::cout << "WAIT..." << std::endl;
            int cli_fd = accept(sock_fd, (sockaddr*)  &cliaddr, (socklen_t*) &size);
            if (cli_fd == 0)
            {
                std::cout << "ERROR!!!" << std::endl;
                break;
            }
            //std::cout << "CORRECT!!!" << std::endl;

            //start time interval - server works!
            startTimer();

            //add socket to the vector
            down();
            sockets.push(cli_fd);
            up();
        }

        return 0;
}
